﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
using System.Web.Http.ModelBinding;
using System.Web.Http.Validation;

namespace HpaApi.WebApi
{
    /// <summary>
    /// 
    /// </summary>
    public class ConcreteParamterBinding : FormatterParameterBinding
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="descriptor"></param>
        /// <param name="formatters"></param>
        /// <param name="bodyModelValidator"></param>
        /// <param name="implement"></param>
        public ConcreteParamterBinding(HttpParameterDescriptor descriptor, IEnumerable<MediaTypeFormatter> formatters, IBodyModelValidator bodyModelValidator, Type implement) :
            base(descriptor,formatters, bodyModelValidator)
        {
            this.Formatters = formatters;
            ImplementType = implement;

        }
        /// <summary>
        /// 
        /// </summary>
        public Type ImplementType { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="metadataProvider"></param>
        /// <param name="actionContext"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            HttpParameterDescriptor paramFromBody = base.Descriptor;

            if (!paramFromBody.ParameterType.IsAssignableFrom(ImplementType))
                throw new Exception(string.Format("{0} is not {1}", ImplementType.ToString(), Descriptor.ParameterType.ToString()));

            Type parameterType = ImplementType;// paramFromBody.ParameterType;
            HttpRequestMessage request = actionContext.ControllerContext.Request;
            IFormatterLogger formatterLogger = new ModelStateFormatterLogger(actionContext.ModelState, paramFromBody.ParameterName);
            return this.ExecuteBindingAsyncCore(metadataProvider, actionContext, paramFromBody, parameterType, request, formatterLogger, cancellationToken);
        }

        private async Task ExecuteBindingAsyncCore(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, HttpParameterDescriptor paramFromBody, Type type, HttpRequestMessage request, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
        {
            request.Properties["MS_FormatterParameterBinding_CancellationToken"] = cancellationToken;
            object model = await this.ReadContentAsync(request, type, this.Formatters, formatterLogger);
            this.SetValue(actionContext, model);
            if (this.BodyModelValidator != null)
            {
                this.BodyModelValidator.Validate(model, type, metadataProvider, actionContext, paramFromBody.ParameterName);
            }
        }
    }
}